#include "log.h"
#include <cstring>
#include <iostream>
#include <session.h>

using namespace std;

std::unique_ptr<char> Protocal::encode(char type, uint32_t bounds, size_t data_size, std::vector<char>& data)
{
    head.version = '1';
    head.id = type;
    head.counter = bounds;
    head.len = sizeof(head) + data_size;
    ALOGD("head len: %ld", head.len);

    unique_ptr<char> ret(new char[head.len]);
    memcpy(ret.get(), &head, sizeof(head));
    memcpy(ret.get() + sizeof(head), data.data(), data_size);
    return ret;
}

void Protocal::decode(std::vector<char> in_data_stream)
{
    const char* s = in_data_stream.data();
    memcpy(&head, s, sizeof(header));

    size_t len = head.len;
    body.assign(s + sizeof(header), s + len);
}

std::string Protocal::getjson()
{
    std::string ret(body.data(), head.counter);
    return ret;
}

std::vector<char> Protocal::getbinary()
{
    std::vector<char> ret;
    if (head.counter < body.size()) {
        ret.assign(body.begin() + head.counter, body.end());
    }
    return ret;
}

size_t chararr_combined_to_sizet(const char* arr, int beginByteIndex, int size)
{
    size_t out = 0;
    // show_memory(const_cast<char*>(arr), size);

    for (int i = beginByteIndex; i < beginByteIndex + size; i++) {
        uint8_t c = *(arr + i);
        int offset = 8 * (i - size);
        size_t t = c << offset;
        out |= t;
    }

    return out;
}

Session::Session(int sessionid)
    : mSt(SESSION::DISCONNECT)
{
    mSessionId = sessionid;
}

bool Session::isComplete(const uv_buf_t* buf, size_t size)
{
    static int count = 0;
    static size_t totalByte = 0;

    if (!count) {
        totalByte = chararr_combined_to_sizet(buf->base, 8, 8);
        ALOGD("协议大小: %ld", totalByte);
        mIn.assign(buf->base, buf->base + size);
    } else {
        mIn.insert(mIn.end(), buf->base, buf->base + size);
    }

    if (size == totalByte) {
        count = 0;
        ALOGD("协议完成");
        return true;
    } else {
        count++;
        totalByte -= size;
        return false;
    }
}

shared_ptr<uv_tcp_t> Session::getSocket()
{
    return mClient;
}

void Session::doWork(const Protocal& data)
{
    cout << "Protocal Header catag id: " << (int)data.head.id << endl;
    cout << "Protocal Body: " << data.body.data() << endl;
}

void Session::doSend(char type, uint32_t bounds, std::vector<char>& body)
{
    Protocal out;
    std::unique_ptr<char> r = out.encode(type, bounds, body.size(), body);

    uv_write_t* writer = new uv_write_t;
    uv_buf_t buf;
    buf.base = r.release();
    buf.len = out.head.len;
    writer->data = buf.base;

    if (uv_is_writable(reinterpret_cast<uv_stream_t*>(mClient.get())) == 1) {
        uv_write(writer, reinterpret_cast<uv_stream_t*>(mClient.get()), &buf, 1, write_cb);
        return;
    }
    ALOGE("The Tcp Socket can't write");
}

void Session::start(std::shared_ptr<uv_tcp_t> handler)
{
    mClient = handler;
    mClient->data = this;
    uv_read_start(reinterpret_cast<uv_stream_t*>(mClient.get()), alloc_cb, read_cb);
}

Session::~Session()
{
    uv_read_stop(reinterpret_cast<uv_stream_t*>(mClient.get()));
    uv_close((uv_handle_t*)(mClient.get()), close_cb);
}

int Session::GetSessionID()
{
    return mSessionId;
}

SESSION Session::status()
{
    return mSt;
}

void Session::setStatus(SESSION s)
{
    mSt = s;
}
